package tcards.android;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

import models.Command;
import models.Kind;
import models.Status;
import models.Tcard;
import models.Command.CommandValue;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.SpannableString;
import android.text.style.UnderlineSpan;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.TabHost;
import android.widget.TabWidget;
import android.widget.Toast;
import android.widget.TabHost.TabSpec;

public class TcardsAndroidActivity extends Activity {
	private BluetoothService mBluetoothService = null;
    private BluetoothAdapter mBluetoothAdapter = null;
    private MenuItem mConnectMenuItem = null;
    private TabHost mTabhost;
    private FrameLayout mMainFrame;
    private StatusPage mStatusPage;
    private EditPage mEditPage;
    private Map<UUID, TcardButton> mSortedTcardButtons;
    private Map<UUID, TcardButton> mUnsortedTcardButtons;
    private boolean mBackPressed;
    private boolean mDeletePressed;
    private UnderlineSpan mUnderline;
    
    // Intent request codes
    private static final int REQUEST_ENABLE_BT = 3;
    
    // Handler Bundle key constants
    public static final int TOAST = 1;
    public static final int COMMAND = 2;
        
    // Debugging
    private static final boolean D = true;    
    private static final String TAG = "TCardsAndroid";
    
    @Override
    public void onCreate(Bundle savedInstanceState) {    	
    	super.onCreate(savedInstanceState);
    	
    	setContentView(R.layout.main);
    	
    	mSortedTcardButtons = new HashMap<UUID, TcardButton>();
    	mUnsortedTcardButtons = new HashMap<UUID, TcardButton>();
    	mBackPressed = false;
    	mDeletePressed = false;
    	mUnderline = new UnderlineSpan();
    	
    	mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        // If the adapter is null, then Bluetooth is not supported
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
            finish();
            return;
        }
    	
        mMainFrame = (FrameLayout) findViewById(R.id.mainFrame);
        mStatusPage = (StatusPage) mMainFrame.findViewById(R.id.statusPage);
        
    	mTabhost = (TabHost) mStatusPage.findViewById(android.R.id.tabhost);
    	mTabhost.setup();
    	
    	TabSpec ts;
    	
    	ts = mTabhost.newTabSpec("tab_all");
    	ts.setIndicator("All");
    	ts.setContent(R.id.allPage);
    	mTabhost.addTab(ts); 
    	
    	ts = mTabhost.newTabSpec("tab_handCrews");
    	ts.setIndicator(Kind.HandCrews.toDisplayString());
    	ts.setContent(R.id.handCrewsPage);
    	mTabhost.addTab(ts);   
    	
    	ts = mTabhost.newTabSpec("tab_engines");
    	ts.setIndicator(Kind.Engines.toDisplayString());
    	ts.setContent(R.id.enginesPage);
    	mTabhost.addTab(ts);
    	
    	ts = mTabhost.newTabSpec("tab_helicopters");
    	ts.setIndicator(Kind.Helicopters.toDisplayString());
    	ts.setContent(R.id.helicoptersPage);
    	mTabhost.addTab(ts);
    	
    	ts = mTabhost.newTabSpec("tab_personnel");
    	ts.setIndicator(Kind.Personnel.toDisplayString());
    	ts.setContent(R.id.personnelPage);
    	mTabhost.addTab(ts);
    	
    	ts = mTabhost.newTabSpec("tab_aircraft");
    	ts.setIndicator(Kind.Aircraft.toDisplayString());
    	ts.setContent(R.id.aircraftPage);
    	mTabhost.addTab(ts);
    	
    	ts = mTabhost.newTabSpec("tab_dozers");
    	ts.setIndicator(Kind.HeavyEquipment.toDisplayString());
    	ts.setContent(R.id.dozersPage);
    	mTabhost.addTab(ts);
    	
    	ts = mTabhost.newTabSpec("tab_misc");
    	ts.setIndicator(Kind.Misc.toDisplayString());
    	ts.setContent(R.id.miscPage);
    	mTabhost.addTab(ts);
    	
    	ts = mTabhost.newTabSpec("tab_generic");
    	ts.setIndicator(Kind.Generic.toDisplayString());
    	ts.setContent(R.id.genericPage);
    	mTabhost.addTab(ts);
    	
    	TabWidget tabWidget = (TabWidget) findViewById(android.R.id.tabs);
    	tabWidget.getChildAt(0).getLayoutParams().width = 93;
    	tabWidget.getChildAt(1).getLayoutParams().width = 143;
    	tabWidget.getChildAt(2).getLayoutParams().width = 116;
    	tabWidget.getChildAt(3).getLayoutParams().width = 139;
    	tabWidget.getChildAt(4).getLayoutParams().width = 130;
    	tabWidget.getChildAt(5).getLayoutParams().width = 199;
    	tabWidget.getChildAt(6).getLayoutParams().width = 184;
    	tabWidget.getChildAt(7).getLayoutParams().width = 160;
    	tabWidget.getChildAt(8).getLayoutParams().width = 116;
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        mConnectMenuItem = menu.findItem(R.id.connect);        
        return true;
    }    
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        /*
        case R.id.discoverable:
        	enableBluetooth();
            ensureDiscoverable();
            break;
        */
        case R.id.connect:
        	enableBluetooth();
        	bluetoothConnect();
        	break;
        }
        return true;
    }
    
    private void enableBluetooth() {
    	if (!mBluetoothAdapter.isEnabled()) {
    		Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    		startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
        }    	
    }

    @Override
    public void onBackPressed() {
    	if (mMainFrame.getChildAt(0) != mStatusPage) {
    		mMainFrame.removeViewAt(0);
    		mMainFrame.addView(mStatusPage);
    	}    	
    	else if(mBackPressed) {
    		mBackPressed = false;
    		if (mBluetoothService != null)
    		{
	    		sendCommand(new Command(CommandValue.CloseConnection));
	    		mBluetoothService.stop();
    		}    		
    		finish();
    	}
    	else {
    		mBackPressed = true;
    		Toast.makeText(this, "Press the back button again to quit", Toast.LENGTH_LONG).show();
    		TimerTask task = new TimerTask() {
    			public void run() {
    				mBackPressed = false;
    			}
    		};
    		Timer t = new Timer("Exit Button Reset");
    		t.schedule(task, 4000); // user has 4 seconds to push back again
    	}
    }
    
    // enable discovery mode function
    /*
    private void ensureDiscoverable() {
        if(D) Log.d(TAG, "ensure discoverable");
        if (mBluetoothAdapter.getScanMode() !=
            BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
            Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
            discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
            startActivity(discoverableIntent);
        }
    }
    */
    
    private void handleIncommingCommands(Message msg)
    {     	
    	switch(msg.what) {
    	case TOAST:
    		Toast.makeText(this, msg.obj.toString(), Toast.LENGTH_LONG).show();
    		break;
    		
    	case COMMAND:
    	    Command cmd = (Command)msg.obj;
    		
			switch(cmd.getCommand()) {
			case Handshake:
				try {
       				mConnectMenuItem.setTitle("Connected");
    				mBluetoothService.sendCommand(new Command(CommandValue.Handshake));
				} catch (Exception e) {
					if(D) Log.d(TAG, "Handshake error, connection failed: " + e.toString());
					mConnectMenuItem.setTitle("Connect");
				}
	    		sendCommand(new Command(CommandValue.RetrieveAllTcards));			
				break;
			
			case TcardUpdate:
			case RetrieveAllTcards:
				attemptUpdate(cmd);
				break;
			case CloseConnection:
				mConnectMenuItem.setTitle("Connect");
				break;
			}	
			break;
    	}  
    }
    
    private void attemptUpdate(final Command cmd) {
    	if (mMainFrame.getChildAt(0) == mStatusPage) {
			for(Tcard tcard : cmd.getTcards()) {
				TcardButton sortedButton = getSortedTcardButton(tcard);
				TcardButton unsortedButton = getUnsortedTcardButton(tcard);
				addTcardButtonToLayout(sortedButton, true);
				addTcardButtonToLayout(unsortedButton, false);
			}
		}
		else {
			TimerTask task = new TimerTask() {
    			public void run() {
    				attemptUpdate(cmd);
    			}
    		};
    		Timer t = new Timer("Scheduled Update");
    		t.schedule(task, 2000); // waits 2 seconds before trying to update again
		}
    }
    
    private void sendCommand(Command cmd)
    {
    	try {
    		mBluetoothService.sendCommand(cmd);
    	}
    	catch(Exception e) 	{
    		Toast.makeText(this, "Error occurred while synching with Main Display:\n" + e.getMessage(), Toast.LENGTH_LONG).show();
    	}    
    }
    
    private void bluetoothConnect()
    {
    	Handler handler = new Handler() {
    		public void handleMessage(Message msg)	{
    			if(D) Log.d(TAG, "Handled: " + msg.toString());
    			handleIncommingCommands(msg);
    		}
    	};
    	
    	mConnectMenuItem.setTitle("Connecting...");
    	mConnectMenuItem.setEnabled(false);
    	
    	mBluetoothService = new BluetoothService(this, handler);
    	
    	String address = mBluetoothAdapter.getBondedDevices().iterator().next().getAddress();
    	BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
    	mBluetoothService.connect(device);
    }
    
    public void updateTcard(Tcard card)
    {
    	Command cmd = new Command(CommandValue.TcardUpdate, card.getId());
    	cmd.getTcards().add(card);
    	sendCommand(cmd);
    }
    
    public void updateTcard(Collection<Tcard> cards)
    {
    	Command cmd = new Command(CommandValue.TcardUpdate);
    	cmd.getTcards().addAll(cards);
    	sendCommand(cmd);
    }
    
    public void showEditPage(Kind kind, Status status) {
		mEditPage = new EditPage(this, kind, status);
		Button deleteButton = (Button) mEditPage.findViewById(R.id.deleteButton);
		deleteButton.setOnLongClickListener(new OnLongClickListener() {
			@Override
			public boolean onLongClick(View v) {
				deleteTcard();
				return true;
			}			
		});
		mMainFrame.removeViewAt(0);
		mMainFrame.addView(mEditPage);		
    }

    public void showEditPage(View v) {
		UnitField chosenField = (UnitField) v.getParent().getParent().getParent().getParent();
		Status status = Status.toStatus(chosenField.getLabel().getText().toString());		
		Kind kind = getCurrentKind();
		if (kind == null) {
			KindChooser kindChooser = new KindChooser(this);
			kindChooser.setStatus(status);
			mMainFrame.removeViewAt(0);
			mMainFrame.addView(kindChooser);
		}
		else {
			showEditPage(kind, status);
		}
    }
    
	public void showEditPage(Tcard t) {
		mEditPage = new EditPage(this, t);
		Button deleteButton = (Button) mEditPage.findViewById(R.id.deleteButton);
		deleteButton.setOnLongClickListener(new OnLongClickListener() {
			@Override
			public boolean onLongClick(View v) {
				deleteTcard();
				return true;
			}			
		});
		mMainFrame.removeViewAt(0);
		mMainFrame.addView(mEditPage);
    }
    
    public void saveTcard(View v) {
    	String errors = mEditPage.validate();
    	if (errors.length() > 0) {
    		Toast.makeText(this, "Tcard cannot be created/updated:\n" + errors, Toast.LENGTH_LONG).show();
    		return;
    	}
    	
    	Tcard tcard = mEditPage.getTcard();
		TcardButton sortedButton = getSortedTcardButton(tcard);
		TcardButton unsortedButton = getUnsortedTcardButton(tcard);
		
		mMainFrame.addView(mStatusPage);
		mMainFrame.removeView(mEditPage);
		
		if(!mEditPage.isUpdate()) {
			addTcardButtonToLayout(sortedButton, true);
			addTcardButtonToLayout(unsortedButton, false);
		}
		else {
			SpannableString text = new SpannableString(tcard.toDisplayString());
			if (sortedButton.getTypeface() == Typeface.DEFAULT_BOLD) {				
				text.setSpan(mUnderline, 0, text.length(), 0);
			}
			sortedButton.setText(text);
			unsortedButton.setText(text);
		}
		updateTcard(tcard);
    }
    
    public void deleteTcard(View v) {
    	if (mDeletePressed) {
    		mDeletePressed = false;
    		deleteTcard();	    	
    	}
    	else {
    		mDeletePressed = true;
			Toast.makeText(this, "Press the delete button again to confirm", Toast.LENGTH_LONG).show();
			TimerTask task = new TimerTask() {
				public void run() {
					mDeletePressed = false;
				}
			};
			Timer t = new Timer("Delete Button Reset");
			t.schedule(task, 4000); // user has 4 seconds to push delete again
	    }
    }
    
    private void deleteTcard() {
    	mMainFrame.addView(mStatusPage);
		mMainFrame.removeView(mEditPage);
    	
    	Tcard tcard = mEditPage.getTcard();
    	UUID id = tcard.getId();
    	if (mSortedTcardButtons.containsKey(id)) {
			TcardButton sortedButton = mSortedTcardButtons.get(id);
			TcardButton unsortedButton = mUnsortedTcardButtons.get(id);
			((ViewGroup) sortedButton.getParent()).removeView(sortedButton);
			((ViewGroup) unsortedButton.getParent()).removeView(unsortedButton);
			mSortedTcardButtons.remove(id);
			mUnsortedTcardButtons.remove(id);			
			
			Command cmd = new Command(CommandValue.TcardDelete, id);
			cmd.getTcards().add(tcard);
			sendCommand(cmd);
    	}
    }
    
    private TcardButton getSortedTcardButton(Tcard tcard) {
    	TcardButton entry = null;
   	
    	if(mSortedTcardButtons.containsKey(tcard.getId())) {
    		entry = mSortedTcardButtons.get(tcard.getId());
    	}
    	else {
    		entry = createTcardButton(tcard);
    		mSortedTcardButtons.put(tcard.getId(), entry);
    	}
    			
		return entry;
    }
    
    private TcardButton getUnsortedTcardButton(Tcard tcard) {
    	TcardButton entry = null;
   	
    	if(mUnsortedTcardButtons.containsKey(tcard.getId())) {
    		entry = mUnsortedTcardButtons.get(tcard.getId());
    	}
    	else {
    		entry = createTcardButton(tcard);
    		mUnsortedTcardButtons.put(tcard.getId(), entry);
    	}
    			
		return entry;
    }
    
    public TcardButton getCorrespondingTcardButton(TcardButton button) {
    	if (mSortedTcardButtons.containsValue(button)) {
    		return mUnsortedTcardButtons.get(button.getTcard().getId());
    	}
    	else {
    		return mSortedTcardButtons.get(button.getTcard().getId());
    	}
    }
 
    private TcardButton createTcardButton(Tcard tcard) {
    	TcardButton button = new TcardButton(this);		
		
		button.setOnTouchListener(new TcardTouchListener(new TcardTouchListener.TcardCommand() {		
			@Override
			public void editCommand(TcardButton button) {
				showEditPage(button.getTcard());
			}
			
			@Override
			public void openCommand(TcardButton button) {
				SpannableString text = new SpannableString(button.getText());
				text.setSpan(mUnderline, 0, text.length(), 0);				
				button.setText(text);
				
				button.setTypeface(Typeface.DEFAULT_BOLD);
				
				TcardsAndroidActivity activity = (TcardsAndroidActivity) button.getContext();
				TcardButton correspondingButton = activity.getCorrespondingTcardButton(button);
				
				correspondingButton.setText(text);
				correspondingButton.setTypeface(Typeface.DEFAULT_BOLD);				
				
				Command cmd = new Command(CommandValue.OpenTcard, button.getTcard().getId());					
		    	sendCommand(cmd);		
			}
						
			@Override
			public void closeCommand(TcardButton button) {
				SpannableString text = new SpannableString(button.getText());
				text.removeSpan(mUnderline);	
				button.setText(text);
				
				button.setTypeface(Typeface.DEFAULT);
				
				TcardsAndroidActivity activity = (TcardsAndroidActivity) button.getContext();
				TcardButton correspondingButton = activity.getCorrespondingTcardButton(button);
				
				correspondingButton.setText(text);
				correspondingButton.setTypeface(Typeface.DEFAULT);	
				
				Command cmd = new Command(CommandValue.CloseTcard, button.getTcard().getId());
		    	sendCommand(cmd); 				
			}
		}));
		
		button.setTcard(tcard);		
		button.setLayoutParams(new LayoutParams(225, 75));
		
		return button;
    }
    
    private void addTcardButtonToLayout(TcardButton button, boolean sort) {
    	UnitPage page;
    	if (sort) {
    		page = getPage(button.getTcard().getKind());
    	}
    	else {
    		page = getPage(null);
    	}
    	Status status = button.getTcard().getStatus();
    	ViewGroup content = page.findField(status).getContent();
		
		if(button.getParent() != null)
			((ViewGroup) button.getParent()).removeView(button);
		
		content.addView(button);
		
		//Move addButton to the bottom of the field
		View addButton = content.findViewById(R.id.addButton);
		content.removeView(addButton);
		content.addView(addButton);
    }
    
    public UnitPage getCurrentPage() {
    	return getPage(getCurrentKind());
    }
 
    public UnitPage getPage(Kind kind) {
    	if (kind == null) {
    		return (UnitPage) findViewById(R.id.allPage);
    	}
    	switch (kind.ordinal()) {
    	case 0:
    		return (UnitPage) findViewById(R.id.handCrewsPage);
    	case 1:
    		return (UnitPage) findViewById(R.id.enginesPage);
    	case 2:
    		return (UnitPage) findViewById(R.id.helicoptersPage);
    	case 3:
    		return (UnitPage) findViewById(R.id.personnelPage);
    	case 4:
    		return (UnitPage) findViewById(R.id.aircraftPage);
    	case 5:
    		return (UnitPage) findViewById(R.id.dozersPage);
    	case 6:
    		return (UnitPage) findViewById(R.id.miscPage);
    	case 7:
    		return (UnitPage) findViewById(R.id.genericPage);
    	default:
    		throw new RuntimeException("Unknown Kind value encountered!");
    	}
    }
    
    // Dirty hack... need to come up with something better!
    public Kind getCurrentKind() {
    	int currentTab = mTabhost.getCurrentTab();
    	if (currentTab == 0) {
    		return null;
    	}    	
    	return Kind.values()[currentTab-1];
    }
}
